import * as json_ from '../util/json_';
import {Strategy} from "../builder/configuration/Strategy";
import {Convertor} from '../converter/DataTypeConvertor'
// import { DBType } from '../db/DB';
import { JavaTypeConvertor, PrecisionScale } from '../converter/JavaTypeConvertor';

const key = {
    base: ['create_by', 'create_time', 'update_by', 'update_time', 'del_flag'],
    delete: 'del_flag',
};

const typeConvertor = (dbType: string)=>{return Convertor[dbType]};

const getPrecisionScale = function (value: string| any): PrecisionScale {
    const numRegex = new RegExp(/.*\((.*)\)$/);
    if (!numRegex.test(value)) return null;
    value = value.match(numRegex)[1];
    let precision, scale;
    if (!isNaN(value)) {
        precision = value;
    } else {
        if (value.indexOf(',') > -1) {
            precision = value.split(',')[0];
            scale = value.split(',')[1];
        }
    }
    precision = !isNaN(precision) ? parseInt(precision) : precision;
    scale = !isNaN(scale) ? parseInt(scale) : scale;

    return new PrecisionScale(precision, scale);

};

const parseType = function (type, dbType) {
    type = type.toLocaleLowerCase();
    let fieldType = type;
    let convertor: any = typeConvertor(dbType);
    for (let key in convertor) {
        if (type.startsWith(key)) {
            let fieldTypeConvertor: JavaTypeConvertor = convertor[key];
            fieldType = fieldTypeConvertor.convert(getPrecisionScale(type));
            break;
        }
    }
    return fieldType;
};

export class Field {
    strategy: Strategy;
    name: string;
    type: string;
    comment: string;
    label: string;
    columnName: string;
    columnType: string;
    columnTypeName: string;
    columnLen: number;
    isPrimary: boolean;
    isUnique: boolean;
    isNotNull: boolean;
    isDeleteKey: boolean;
    isBaseKey: boolean;
    isInsertFillKey: boolean;
    isInsertUpdateFillKey: boolean;
    isUpdateFillKey: boolean;
    isRangeFieldKey: boolean;
    isFeishuBiTableField: boolean;
    isConvert: boolean;
    isDate: boolean;

    constructor(strategy, name, type, comment, columnName, columnType, isPrimary, isUnique, isNotNull) {
        this.strategy = strategy;
        this.name = name;
        this.type = type;
        this.comment = comment;
        this.label = this._renderLabel();
        this.columnName = this.getColumnName(columnName);
        this.columnType = columnType;
        this.columnTypeName = this.getColumnTypeName();
        this.columnLen = this.getColumnLen();
        this.isPrimary = isPrimary;
        this.isUnique = isUnique;
        this.isNotNull = isNotNull;
        this.isDeleteKey = this._isDeleteKey();
        this.isBaseKey = this._isBaseKey();
        this.isInsertFillKey = this._isInsertFillKey();
        this.isInsertUpdateFillKey = this._isInsertUpdateFillKey();
        this.isUpdateFillKey = this._isUpdateFillKey();
        this.isRangeFieldKey = this._isRangeFieldKey();
        this.isFeishuBiTableField = this._isFeishuBiTableField();
        this.isConvert = this._isConvert();
        this.isDate = this._isDate();
    }

    getColumnName(column){
        return column.toLowerCase();
    }

    getColumnLen() {
        let precisionScale = getPrecisionScale(this.columnType);
        return precisionScale ? precisionScale[0] : undefined;
    }

    getColumnTypeName(){
        let render = (column)=>{
            let caster = {
                datetime: 'timestamp',
                text: 'varchar',
                int: 'integer',
            } ;
            return caster[column] || column;
        };
        let columnType = this.columnType.split('(')[0];
        return render(columnType);
    }

    _renderLabel() {
        if(!this.comment){
            return this.name;
        }
        const text = this.comment;
        const maxLength = 10;
        let label: string = text.substring(0, text.length > maxLength ? maxLength : text.length);
        if(label.includes(' ')){
          label = label.split(' ')[0];
        }
        const handle = (regexp: RegExp, str: string) => {
            if(regexp.test(str)){
                const matchArr = regexp.exec(str);
                if(matchArr){
                return matchArr[1];
                }
            }
            return str;
        }
        const regexp1 = /(\S+)([[|【|(|（]).*$/;
        const regexp2 = /(\S+)([:|：|;|；]).*$/;
        label = handle(regexp1, label);
        label = handle(regexp2, label);

        return label;
    }

    _isDeleteKey() {
        let deleteColumn = this.strategy.baseDtoConfig.deleteColumn || key.delete;
        return deleteColumn === this.columnName;
    }

    _isBaseKey() {
        if(!this.strategy.baseDtoConfig.hasBaseDto) {
            return false;
        }
        let baseColumns = this.strategy.baseDtoConfig.baseColumns || key.base;
        return baseColumns.indexOf(this.columnName) > -1;
    }
    _isInsertFillKey(){
        if(!this.strategy.fieldFillConfig.hasFieldFill) {
            return false;
        }
        let baseColumns = this.strategy.fieldFillConfig.insertFillColumns;
        return baseColumns.indexOf(this.columnName) > -1;
    }
    _isInsertUpdateFillKey(){
        if(!this.strategy.fieldFillConfig.hasFieldFill) {
            return false;
        }
        let baseColumns = this.strategy.fieldFillConfig.insertUpdateFillColumns;
        return baseColumns.indexOf(this.columnName) > -1;
    }
    _isUpdateFillKey(){
        if(!this.strategy.fieldFillConfig.hasFieldFill) {
            return false;
        }
        let baseColumns = this.strategy.fieldFillConfig.updateFillColumns;
        return baseColumns.indexOf(this.columnName) > -1;
    }
    _isRangeFieldKey(){
        if(!this.strategy.rangeFieldConfig.hasRangeField) {
            return false;
        }
        let baseColumns = this.strategy.rangeFieldConfig.rangeFieldColumns;
        return baseColumns.indexOf(this.columnName) > -1;
    }

    _isFeishuBiTableField(){
        if(this.strategy.feishuBiTableFieldConfig.excludes.length ===0 ) {
            return false;
        }
        let excludeColumns = this.strategy.feishuBiTableFieldConfig.excludes;
        return excludeColumns.indexOf(this.columnName) === -1;
    }
    _isConvert() {
        return this.name === this.columnName;
    }

    _isDate() {
        return this.type === 'Date';
    }

    static parseJson(json, strategy: Strategy) {
        return json_.parseJsonRender(json, function ({name, type, primary, unique, notnull, comment}) {
            name = name.toLowerCase();
            return new Field(strategy, strategy.renderCamelCase(name, false), parseType(type, strategy.dbType), comment, name, type, primary, unique, notnull);
        });
    }
}

export class Entity {
    project: string;
    module: string;
    name: string;
    beanName: string;
    tableName: string;
    comment: string;
    fields: Field[];
    constructor(tableName, comment, fields, strategy: Strategy) {
        this.project = strategy.getProject(tableName);
        this.module = strategy.getModule(tableName);
        this.name = strategy.getEntity(tableName);
        this.beanName = strategy.getEntityBeanName(tableName);
        this.tableName = tableName;
        this.comment = comment;
        this.fields = fields;
    }

    static parseJson(json, strategy: Strategy) {
        return json_.parseJsonRender(json, function({name, comment, columns}) {
            return new Entity(name, comment, Field.parseJson(columns, strategy), strategy);
        });
    }
}
